﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Saxxon.LibDoomSharp.Lumps
{
    /// <summary>
    /// Abstract class that provides list and serialization functionality for lumps that use tables.
    /// </summary>
    abstract public class TableLump<T> : SerializableBase, IList<T> where T : SerializableBase, new()
    {
        /// <summary>
        /// Base constructor.
        /// </summary>
        public TableLump()
        {
            Entries = new List<T>();
        }

        /// <summary>
        /// Base constructor that pulls from Lump data.
        /// </summary>
        public TableLump(Lump sourceLump)
        {
            Data = sourceLump.Data;
        }

        /// <summary>
        /// Get or set the raw lump data.
        /// </summary>
        public byte[] Data
        {
            get
            {
                using (MemoryStream mem = new MemoryStream())
                {
                    Write(mem);
                    mem.Flush();
                    return mem.ToArray();
                }
            }
            set
            {
                using (MemoryStream mem = new MemoryStream(value))
                {
                    Read(mem, (int)mem.Length);
                }
            }
        }

        /// <summary>
        /// Internal entry list.
        /// </summary>
        protected List<T> Entries { get; set; }

        /// <summary>
        /// Retrieve the length of a table entry. Must be overridden.
        /// </summary>
        abstract protected int EntryLength { get; }

        #region IList Inherited Methods
        public int IndexOf(T item)
        {
            return Entries.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            Entries.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            Entries.RemoveAt(index);
        }

        public T this[int index]
        {
            get
            {
                return Entries[index];
            }
            set
            {
                Entries[index] = value;
            }
        }

        public void Add(T item)
        {
            Entries.Add(item);
        }

        public void Clear()
        {
            Entries.Clear();
        }

        public bool Contains(T item)
        {
            return Entries.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            Entries.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return Entries.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(T item)
        {
            return Entries.Remove(item);
        }

        public IEnumerator<T> GetEnumerator()
        {
            return Entries.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Entries.GetEnumerator();
        }
        #endregion

        /// <summary>
        /// Read table data.
        /// </summary>
        protected override void Read(Stream stream, int length)
        {
            Entries = new List<T>();
            DataReader reader = new DataReader(stream);
            byte[] rawData = reader.ReadBytes(length);

            using (MemoryStream mem = new MemoryStream(rawData))
            {
                // initialize stream params
                long streamLength = mem.Length;
                int entryLength = EntryLength;
                int total = length / entryLength;

                // read each entry
                for (int i = 0; i < total; i++)
                {
                    T item = new T();
                    item.ReadFrom(mem, entryLength);
                    Entries.Add(item);
                }
            }
        }

        /// <summary>
        /// Convert the table data to a lump.
        /// </summary>
        virtual public Lump ToLump()
        {
            Lump result = new Lump();
            result.Data = this.Data;
            return result;
        }

        /// <summary>
        /// Write table data.
        /// </summary>
        protected override void Write(Stream stream)
        {
            foreach (T item in Entries)
            {
                item.WriteTo(stream);
            }
        }
    }
}
